// Copyright (C) Calum Grant 2008

#include "cppscript"
#include "dynamic/extensions.hpp"


namespace dynamic
{
namespace types
{
	/// Implements a proxy to a shared variable.
	/** This object is stored internally in the var (in \ref var::m_variant), 
		but contains a pointer to an object of type shared_var_impl.
		\ingroup impl
		*/
	template<typename Pointer>
	class shared_var_proxy : public var_impl
	{
		Pointer m_shared;
	public:
		shared_var_proxy(shared_var_impl * p) : m_shared(p) { }

		static shared_var_impl * get_pointer(shared_var_impl * p) { return p; }
		static shared_var_impl * get_pointer(gc::root_ref<shared_var_impl> & p) { return p.get(); }

		shared_var_impl * shared_var() { return get_pointer(m_shared); }

		var as_root() { return var(shared_var_proxy<gc::root_ref<shared_var_impl> >(shared_var()), var::assign_impl()); }
		var as_nonroot() { return var(shared_var_proxy<shared_var_impl*>(shared_var()), var::assign_impl()); }

		void mark_reachable(gc::garbage_collector & gc) { m_shared->mark_reachable(gc); }

		// Proxy methods
		var call() { return m_shared->call(); }
		var call(const var & a0) { return m_shared->call(a0); }
		var call(const var & a0, const var & a1) { return m_shared->call(a0,a1); }
		var call(const var & a0, const var & a1, const var & a2) { return m_shared->call(a0,a1,a2); }
		var call(const var & a0, const var & a1, const var & a2, const var & a3) { return m_shared->call(a0,a1,a2,a3); }
		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4) { return m_shared->call(a0,a1,a2,a3,a4); }
		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5) { return m_shared->call(a0,a1,a2,a3,a4,a5); }
		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6) { return m_shared->call(a0,a1,a2,a3,a4,a5,a6); }
		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7) { return m_shared->call(a0,a1,a2,a3,a4,a5,a6,a7); }
		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7, const var & a8) { return m_shared->call(a0,a1,a2,a3,a4,a5,a6,a7,a8); }
		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7, const var & a8, const var & a9) { return m_shared->call(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9); }

		var_cmp_index comparison_index() { return m_shared->comparison_index(); }
		var_cmp_result compare(const var & other) { return m_shared->compare(other); }
		var get_member(var const &m) { return m_shared->get_member(m); }
		void set_member(var const &n, var const &m) { m_shared->set_member(n,m); }
		var get_member(const char * m) { return m_shared->get_member(m); }
		void set_member(const char* n, var const &m) { m_shared->set_member(n,m); }
		var get_member(int m) { return m_shared->get_member(m); }
		void set_member(int n, var const &m) { m_shared->set_member(n,m); }

		void push_back(var const&v) { m_shared->push_back(v); }
		var pop_back() { return m_shared->pop_back(); }
		void push_front(var const&v) { return m_shared->push_front(v); }
		var pop_front() { return m_shared->pop_front(); }
		void insert(const var & v) { m_shared->insert(v); }
		void insert(const var & v, const var & w) { m_shared->insert(v,w); }

		var front() { return m_shared->front(); }
		var back() { return m_shared->back(); }

		var begin() { return m_shared->begin(); }
		var end() { return m_shared->end(); }
		int size() { return m_shared->size(); }
		bool empty() { return m_shared->empty(); }
		var rbegin() { return m_shared->rbegin(); }
		var rend() { return m_shared->rend(); }
		void clear() { m_shared->clear(); }

		int bind_args() { return m_shared->bind_args(); }

		bool contains(const var&v) { return m_shared->contains(v); }
		void erase(const var&v) { m_shared->erase(v); }
		void mixin(const var&v) { m_shared->mixin(v); }

		void output(ostream & ss) { m_shared->output(ss); }
		void output(wostream & ss) { m_shared->output(ss); }
		void short_output(ostream & ss) { m_shared->short_output(ss); }
		void short_output(wostream & ss) { m_shared->short_output(ss); }
		int as_int() { return m_shared->as_int(); }
		double as_double() { return m_shared->as_double(); }
		bool as_bool() { return m_shared->as_bool(); }
		void * as_ptr() { return m_shared->as_ptr(); }
		var deref() { return m_shared->deref(); }
		var key() { return m_shared->key(); }
		var value() { return m_shared->value(); }
		var clone() { return m_shared->clone(); }
		void resize(std::size_t n) { m_shared->resize(n); }

		var op_add(const var &v) { return m_shared->op_add(v); }
		var keys() { return m_shared->keys(); }
		var values() { return m_shared->values(); }

		var op_neg() { return m_shared->op_neg(); }
		var op_pos() { return m_shared->op_pos(); }

		var op_sub(const var &v) { return m_shared->op_sub(v); }
		var op_mul(const var &v) { return m_shared->op_mul(v); }
		var op_div(const var &v) { return m_shared->op_div(v); }
		var op_mod(const var &v) { return m_shared->op_mod(v); }
		var op_lshift(const var&v) { return m_shared->op_lshift(v); }
		var op_rshift(const var&v) { return m_shared->op_rshift(v); }
		var op_and(const var &v) { return m_shared->op_and(v); }
		var op_or(const var &v) { return m_shared->op_or(v); }
		var op_xor(const var &v) { return m_shared->op_xor(v); }
		var op_inv() { return m_shared->op_inv(); }

		void op_inc() { return m_shared->op_inc(); }
		void op_dec() { return m_shared->op_dec(); }

		void assign_add(const var&v) { m_shared->assign_add(v); }
		void assign_sub(const var&v) { m_shared->assign_sub(v); }
		void assign_mul(const var&v) { m_shared->assign_mul(v); }
		void assign_div(const var&v) { m_shared->assign_div(v); }
		void assign_mod(const var&v) { m_shared->assign_mod(v); }
		void assign_lshift(const var&v) { m_shared->assign_lshift(v); }
		void assign_rshift(const var&v) { m_shared->assign_rshift(v); }
		void assign_and(const var&v) { m_shared->assign_and(v); }
		void assign_or(const var&v) { m_shared->assign_or(v); }
		void assign_xor(const var&v) { m_shared->assign_xor(v); }

		std::string class_name() { return m_shared->class_name(); }
		void pickle(pickler&p) { m_shared->pickle(p); }
		void unpickle(unpickler&u) { m_shared->unpickle(u); }

		void copy_to(void * dest) const
		{ new(dest) shared_var_proxy(*this); }

		var member_inc(const char *m) { return m_shared->member_inc(m); }
		var member_inc(const char *m, int i) { return m_shared->member_inc(m,i); }
		var member_dec(const char *m) { return m_shared->member_dec(m); }
		var member_dec(const char *m, int i) { return m_shared->member_dec(m,i); }
		var member_add(const char * member, const var & i) { return m_shared->member_add(member, i); }
		var member_sub(const char * member, const var & i) { return m_shared->member_sub(member, i); }

		var member_call(const char*m) { return m_shared->member_call(m); }
		var member_call(const char*m, const var & a0) { return m_shared->member_call(m,a0); }
		var member_call(const char*m, const var & a0, const var & a1) { return m_shared->member_call(m,a0,a1); }
		var member_call(const char*m, const var & a0, const var & a1, const var & a2) { return m_shared->member_call(m,a0,a1,a2); }
		var member_call(const char*m, const var & a0, const var & a1, const var & a2, const var & a3) { return m_shared->member_call(m,a0,a1,a2,a3); }
		var member_call(const char*m, const var & a0, const var & a1, const var & a2, const var & a3, const var & a4) { return m_shared->member_call(m,a0,a1,a2,a3,a4); }
		var member_call(const char*m, const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5) { return m_shared->member_call(m,a0,a1,a2,a3,a4,a5); }
		var member_call(const char*m, const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6) { return m_shared->member_call(m,a0,a1,a2,a3,a4,a5,a6); }
		var member_call(const char*m, const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7) { return m_shared->member_call(m,a0,a1,a2,a3,a4,a5,a6,a7); }
		var member_call(const char*m, const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7, const var & a8) { return m_shared->member_call(m,a0,a1,a2,a3,a4,a5,a6,a7,a8); }
		var member_call(const char*m, const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7, const var & a8, const var & a9)  { return m_shared->member_call(m,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9); }

		int max_args() { return m_shared->max_args(); }

		var proxy() { return m_shared->proxy(); }
		var enumerator() { return m_shared->enumerator(); }
		var reverse_enumerator() { return m_shared->reverse_enumerator(); }

		void reserve(std::size_t s) { m_shared->reserve(s); }
		std::size_t capacity() { return m_shared->capacity(); }
		void get_range(std::size_t container_size, std::pair<std::size_t,std::size_t> & out) { m_shared->get_range(container_size, out); }
	};

	/// A root pointer to an object.
	/** This ensures that the object is not destroyed by the garbage collector
		by adding to its \ref dynamic::gc::gc_object::root_ref_count. */
	typedef shared_var_proxy<gc::root_ref<shared_var_impl> > root_shared_var;
}
}


var::var(shared_var_impl * i) : m_variant(types::root_shared_var(i)) 
{ 
}


var::var(const char * str) : m_variant(internal::create_string(str).m_variant) 
{ 
}


var::var(const std::string & str) : m_variant(internal::create_string(str).m_variant) 
{ 
}


var::var(const wchar_t * str) : m_variant(internal::create_string(str).m_variant) 
{ 
}


var::var(const wstring & str) : m_variant(internal::create_string(str).m_variant) 
{ 
}


var::var(f0 fn) : m_variant(function(fn).m_variant) { }
var::var(f1 fn) : m_variant(function(fn).m_variant) { }
var::var(f2 fn) : m_variant(function(fn).m_variant) { }
var::var(f3 fn) : m_variant(function(fn).m_variant) { }
var::var(f4 fn) : m_variant(function(fn).m_variant) { }
var::var(f5 fn) : m_variant(function(fn).m_variant) { }
var::var(f6 fn) : m_variant(function(fn).m_variant) { }
var::var(f7 fn) : m_variant(function(fn).m_variant) { }
var::var(f8 fn) : m_variant(function(fn).m_variant) { }
var::var(f9 fn) : m_variant(function(fn).m_variant) { }
var::var(f10 fn) :m_variant(function(fn).m_variant) { }

var::var(vf0 fn) : m_variant(internal::function(fn).m_variant) { }
var::var(vf1 fn) : m_variant(internal::function(fn).m_variant) { }
var::var(vf2 fn) : m_variant(internal::function(fn).m_variant) { }
var::var(vf3 fn) : m_variant(function(fn).m_variant) { }
var::var(vf4 fn) : m_variant(function(fn).m_variant) { }
var::var(vf5 fn) : m_variant(function(fn).m_variant) { }
var::var(vf6 fn) : m_variant(function(fn).m_variant) { }
var::var(vf7 fn) : m_variant(function(fn).m_variant) { }
var::var(vf8 fn) : m_variant(function(fn).m_variant) { }
var::var(vf9 fn) : m_variant(function(fn).m_variant) { }
var::var(vf10 fn): m_variant(function(fn).m_variant) { }


var::var(const internal::extender & e) : m_variant( e.deref().m_variant ) 
{ 
}


internal::var_member<var> var::operator[](const var & member) const
{
	return internal::var_member<var>(*this, member);
}


internal::var_member<int> var::operator[](int member) const
{
	return internal::var_member<int>(*this, member);
}
